﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.SharePoint.Taxonomy;
using VNTG.TaxonomyUploader.model;
using Microsoft.SharePoint;

namespace VNTG.TaxonomyUploader
{

    /// <summary>
    /// This class wil import the parsed csv file in to the meta data.
    /// </summary>
    class InsertIntoTaxonomy
    {
        private string _group { get; set; }
        private string _store { get; set; }


        /// <summary>
        /// this function is the constructor and will set the termstore and group
        /// where the meta will be entered
        /// </summary>
        /// <param name="iStore">Taxonomy TermStore</param>
        /// <param name="iGroup">TermStore Group</param>
        public InsertIntoTaxonomy(string iStore, string iGroup)
        {
            this._group = iGroup;
            this._store = iStore;
        }

        /// <summary>
        /// This function will create a list of MyTerm
        /// it will represent the taxonomy tree
        /// </summary>
        /// <param name="list">parsed csv file</param>
        private void StartImport(List<string[]> list)
        {

            List<MyTerm> termList = new List<MyTerm>();

            MyTerm term = new MyTerm();
            MyTerm childterm = null;
            MyTerm parentterm = null;

            for (int i = 0; i < list.Count; i++)
            {
                for (int j = 0; j < list[i].Count(); j++)
                {
                    if (j == 0 && list[i][j] != string.Empty)
                    {
                        if (i != 0)
                        {
                            termList.Add(term);
                        }
                        term = new MyTerm();
                        term.Language.Add(0, list[i][j]);
                    }
                    else if (j > 1)
                    {
                        var lang = GetLanguages(list[i][j]);

                        if (j == 2)
                        {
                            parentterm = term;
                        }
                        else
                        {
                            parentterm = childterm;
                        }

                        childterm = parentterm.GetTerm(lang[1033]);

                        if (childterm.Language.Count == 0)
                        {
                            childterm.Language = lang;
                            parentterm.Childeren.Add(childterm);
                        }
                    }
                }
            }

            termList.Add(term);


            InstertIntoTaxonomy(termList);

        }

        /// <summary>
        /// This function will run with elevatedPriviliges and
        /// It wil fetch/create a termset and insert/add terms
        /// </summary>
        /// <param name="termList"></param>
        private void InstertIntoTaxonomy(List<MyTerm> termList)
        {

            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite s = new SPSite(SPContext.Current.Site.Url))
                {
                    //create new taxonomy session 
                    TaxonomySession ses = new TaxonomySession(s, false);

                    //get the store that was selected in the webpart
                    TermStore store = ses.TermStores[_store];

                    //get the group that was selected in the webpart
                    Group group = store.Groups[_group];

                    //loop the list with the terms that need to be added
                    foreach (MyTerm term in termList)
                    {
                        //get the thermset name
                        string title = term.Language[0];

                        //get the termset
                        TermSet set = GetTermSet(title, group);

                        //check if the termset has childeren
                        if (term.HasChilderen())
                        {
                            //add child item to the termset
                            AddChildTerm(set, term.Childeren);
                        }
                    }

                    //store all changes 
                    store.CommitAll();
                }
            });


        }

        /// <summary>
        /// This will ad childterms to the termset
        /// </summary>
        /// <param name="set">The termset where the childitems need to be inserted</param>
        /// <param name="list">The list of child items</param>
        private void AddChildTerm(TermSet set, List<MyTerm> list)
        {
            //loop the child items
            foreach (MyTerm item in list)
            {
                //get the term
                Term term = GetChildTerm(item.Language[1033], null, set);

                //set the term labels
                SetTermLanguages(term, item);

                //check for childeren
                if (item.HasChilderen())
                {
                    foreach (MyTerm oTerm in item.Childeren)
                    {
                        AddChildTerm(term, oTerm);
                    }
                }
            }
        }

        /// <summary>
        /// This function wil set the term labels.
        /// If a label doesn't excist, a new label will be created.
        /// </summary>
        /// <param name="term"></param>
        /// <param name="item"></param>
        private void SetTermLanguages(Term term, MyTerm item)
        {
            foreach (int key in item.Language.Keys)
            {
                bool labelExcist = false;

                foreach (Microsoft.SharePoint.Taxonomy.Label label in term.Labels)
                {
                    if (label.Language == key)
                    {
                        labelExcist = true;
                        break;
                    }
                }

                if (!labelExcist)
                {
                    term.CreateLabel(item.Language[key], key, true);
                }
            }
        }
        
        /// <summary>
        /// This will ad childterms to the parent term
        /// </summary>
        /// <param name="term">ParentTerm</param>
        /// <param name="MyTerm">Term to be added</param>
        private void AddChildTerm(Term term, MyTerm MyTerm)
        {
            Term childTerm = null;
            
            //get the child term
            childTerm = GetChildTerm(MyTerm.Language[1033], term, null);
            
            //set the labels
            SetTermLanguages(childTerm, MyTerm);

            //check for childeren
            if (MyTerm.HasChilderen())
            {
                //loop the chilideren of the childterm
                foreach (MyTerm item in MyTerm.Childeren)
                {
                    
                    //It will call the function it is in to add child terms to the current childterm
                    AddChildTerm(childTerm, item);
                }
            }
        }

        /// <summary>
        /// This function will search for the child term.
        /// If the term doesn't excist it wil create a new term
        /// </summary>
        /// <param name="name">Term to search for</param>
        /// <param name="term">Parent term if you don't have a term use null</param>
        /// <param name="set">Termset to search the term in, when you don't a termset use null</param>
        /// <returns>Taxonomy Term</returns>
        private Term GetChildTerm(string name, Term term, TermSet set)
        {
            Term result = null;

            TermCollection terms;

            if (set != null)
            {
                terms = set.Terms;
            }
            else
            {
                terms = term.Terms;
            }

            if (terms.Count != 0)
            {
                foreach (Term item in terms)
                {
                    if (item.Name == name)
                    {
                        result = item;
                        break;
                    }
                }
            }
            if (result == null)
            {
                result = set == null ? term.CreateTerm(name, 1033) : set.CreateTerm(name, 1033);
            }

            return result;
        }

        /// <summary>
        /// This function will get the termset.
        /// If the termset doesn't excist it will create a new termset
        /// this function returns a TermSet
        /// </summary>
        /// <param name="title">The title of the termset</param>
        /// <param name="termSet">The group to search in</param>
        /// <returns></returns>
        private TermSet GetTermSet(string title, Group termSet)
        {
            TermSet result = null;

            if (termSet.TermSets.Count != 0)
            {
                foreach (TermSet set in termSet.TermSets)
                {
                    if (string.Equals(set.Name, title))
                    {
                        result = set;
                        break;
                    }
                }

                if (result == null)
                {
                    result = termSet.CreateTermSet(title);
                }
            }
            else
            {
                result = termSet.CreateTermSet(title);
            }

            return result;
        }

        /// <summary>
        /// This function is used to parse the languages
        /// </summary>
        /// <param name="input">languages to be added </param>
        /// <returns></returns>
        private Dictionary<int, string> GetLanguages(string input)
        {
            Dictionary<int, string> result = new Dictionary<int, string>();

            string[] parseLang;
            string[] lang;

            parseLang = input.Split(';');

            foreach (string item in parseLang)
            {
                lang = item.Split(':');
                result.Add(Convert.ToInt32(lang[0]), lang[1]);
            }

            return result;
        }

        /// <summary>
        /// This function will parse the CSV file and start the import process
        /// </summary>
        /// <param name="inputstream">The CSV file stream</param>
        public void StartParseCSVAndImport(Stream inputstream)
        {
            List<string[]> parsedData = new List<string[]>();

            using (StreamReader readFile = new StreamReader(inputstream))
            {
                try
                {
                    string line;
                    string[] row;

                    while ((line = readFile.ReadLine()) != null)
                    {
                        row = line.Split(',');
                        parsedData.Add(row);
                    }
                }
                catch (Exception ex)
                {

                    Console.WriteLine(ex.ToString());
                }
            }

            StartImport(parsedData);
        }

    }
}
